﻿using System;
using System.Collections.Generic;
using System.IO;
using System.ServiceModel.Description;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using UFIDA.U9.Cust.Pub.WS.ProxyService.Json;
using UFIDA.U9.WSS.ProxyService.Behavior.WebHttp.Base;
using UFIDA.U9.WSS.ProxyService.Exceptions;
using UFIDA.U9.WSS.ProxyService.Json;

namespace UFIDA.U9.WSS.ProxyService.Behavior.WebHttp
{
    public class GeneralServiceDispatchFormatter : NewtonsoftJsonDispatchFormatter
    {
        private readonly bool _useDataContract;
        private Dictionary<string, int> _parameterNames;

        public GeneralServiceDispatchFormatter(OperationDescription operation, bool useDataContract = false) :
            base(operation)
        {
            _useDataContract = useDataContract;
        }

        /// <summary>
        ///     解析参数
        /// </summary>
        /// <param name="rawBody"></param>
        /// <param name="parameters"></param>
        protected override void ParseParameters(byte[] rawBody, object[] parameters)
        {
            using (MemoryStream ms = new MemoryStream(rawBody))
            {
                using (StreamReader sr = new StreamReader(ms))
                {
                    JsonSerializer serializer = JsonHelper.GetDefaultJsonSerializer();
                    if (parameters.Length == 1)
                    {
                        // single parameter, assuming bare
                        parameters[0] = serializer.Deserialize(sr, _operation.Messages[0].Body.Parts[0].Type);
                    }
                    else
                    {
                        if (_parameterNames == null)
                        {
                            _parameterNames = new Dictionary<string, int>();
                            int operationParameterCount = _operation.Messages[0].Body.Parts.Count;
                            if (operationParameterCount > 1)
                            {
                                for (int i = 0; i < operationParameterCount; i++)
                                {
                                    _parameterNames.Add(_operation.Messages[0].Body.Parts[i].Name, i);
                                }
                            }
                        }

                        // multiple parameter, needs requests to be wrapped
                        JsonReader reader = new JsonTextReader(sr);
                        reader.Read();
                        if (reader.TokenType != JsonToken.StartObject)
                        {
                            throw new WSException(ExceptionCodes.ServiceRequestDataFormatException,
                                "input needs to be wrapped in an object");
                        }

                        reader.Read();
                        while (reader.TokenType == JsonToken.PropertyName)
                        {
                            string parameterName = reader.Value as string;
                            reader.Read();
                            if (!string.IsNullOrEmpty(parameterName) && _parameterNames.ContainsKey(parameterName))
                            {
                                int parameterIndex = _parameterNames[parameterName];
                                try
                                {
                                    parameters[parameterIndex] = serializer.Deserialize(reader,
                                        _operation.Messages[0].Body.Parts[parameterIndex].Type);
                                }
                                catch (Exception ex)
                                {
                                    Logger.Error(ex);
                                    throw new WSException(ExceptionCodes.ServiceRequestDataFormatException,
                                        string.Format("request data:{0} is incorrect", parameterName), ex);
                                }
                            }
                            else
                            {
                                reader.Skip();
                            }

                            reader.Read();
                        }

                        reader.Close();
                    }

                    sr.Close();
                    ms.Close();
                }
            }
        }

        protected override IContractResolver GetSerializeReplyContractResolver()
        {
            return new BaseContractResolver(_useDataContract);
        }
    }
}